//
//  AW2EIMMessageStream.m
//  AtWork2
//
//  Created by 吕硕 on 15/4/8.
//  Copyright (c) 2015年 Foreveross. All rights reserved.
//

#import "AWClientStream.h"
#import "AWMessage.h"
#import "AWProtocol.h"
#import "AWSessionMetaData.h"
#import "GCDAsyncSocket.h"
#import "ATWUtil.h"
#import "AWMessageResolver.h"
#import "ATWImApplication.h"

static const NSTimeInterval kHeartBeatTimerInterval = 100;

@interface AWClientStream () <GCDAsyncSocketDelegate> {
    dispatch_queue_t _socketDelegateQueue;
}

@property (nonatomic, strong, readonly) GCDAsyncSocket *theSocket;

@property (nonatomic, weak) id<AW2ClientStreamDelegate> delegate;

/** 消息缓存区 */
@property (nonatomic, strong) NSMutableData *buffer;

/** 消息体长度 */
@property (nonatomic, assign) UInt16 messgageBodySize;

/** 心跳定时器 */
@property (nonatomic, strong) NSTimer *heartBeatTimer;

@property (nonatomic, assign, readwrite, getter=isNormalCancelled) BOOL normalCancelled;

@property (nonatomic, strong, readwrite) AWSessionMetaData *sessionMetadata;

@property (nonatomic, assign, readwrite) int64_t sendingPingTimestamp;

@end

@implementation AWClientStream

- (instancetype)initWithAW2MessageStreamDelegate:(id<AW2ClientStreamDelegate>)delegate {
    if (self = [super init]) {
        _streamConnectionState = AW2StreamConnectionStateDisconnected;
         _socketDelegateQueue = dispatch_queue_create("com.forveross.atwork.socket.queue", NULL);
        _theSocket = [[GCDAsyncSocket alloc] initWithDelegate:self delegateQueue:_socketDelegateQueue];
        self.delegate = delegate;
    }
    return self;
}

- (void)dealloc {
    _socketDelegateQueue = nil;
    [self p_removeHeartBeat];
    [self.theSocket disconnect];
}

- (void)connectWithSessionMedaData:(AWSessionMetaData *)sessionMetaData {
    self.sessionMetadata = sessionMetaData;
    [self p_connectionStream];
}

- (void)p_connectionStream {
    [self abnormalCancelled];
    if (_theSocket.isConnected == true && self.sessionMetadata.secret == nil) {
        return;
    }
    
    NSError *err = nil;
    if ([_theSocket connectToHost:self.sessionMetadata.host onPort:self.sessionMetadata.port withTimeout:5 error:&err]) {
        if (err) {
            _streamConnectionState = AW2StreamConnectionStateDisconnected;
        }
        _streamConnectionState = AW2StreamConnectionStateConnected;
        [self sendMessageData:[self authData] withProtocolType:AWProtocolConnectType];
    }
    [_theSocket readDataWithTimeout:-1 tag:0];
}

- (NSData *)authData {
    UInt64 time = [ATWUtil dateOfServerTimestamp];
    UInt64 nonce = time;
    NSDictionary *message = @{@"tenantId":self.sessionMetadata.tenantId,
                              @"clientId":self.sessionMetadata.username,
                              @"deviceId" : self.sessionMetadata.deviceId,
                              @"nonce":@(nonce),
                              @"timestamp":@(time),
                              @"signature": [ATWUtil base64DigestUsingMd5WithString:[NSString stringWithFormat:@"%@%lld%lld", self.sessionMetadata.secret, time, nonce]]
                              };
   return [NSJSONSerialization dataWithJSONObject:message options:NSJSONWritingPrettyPrinted error:nil];
}

- (void)sendMessage:(AWMessage *)message {
    message.deliveryTime = [ATWUtil dateOfServerTimestamp];
    [self sendMessageData:[message buildData] withProtocolType:AWProtocolPostType];
}

- (void)sendMessageData:(NSData *)data withProtocolType:(UInt8)protocolType {
    UInt8 firstByte = protocolType << 4 | 0;
    UInt8 digestSize = 0;
    UInt16 bodySize = CFSwapInt16HostToBig(data.length);
    
    NSMutableData *messageData = [[NSMutableData alloc] initWithCapacity:data.length + 4];
    [messageData appendBytes:&firstByte length:1];
    [messageData appendBytes:&digestSize length:1];
    [messageData appendBytes:&bodySize length:2];
    [messageData appendData:data];
    [self sendMessage:messageData withTag:0];
}

- (void)sendMessage:(NSData *)message withTag:(long)tag {

    [_theSocket writeData:message withTimeout:5 tag:tag];
    if (self.delegate && [self.delegate respondsToSelector:@selector(stream:didSendMessage:withTag:)])
    {
        [self.delegate stream:self didSendMessage:message withTag:tag];
    }
    [_theSocket readDataWithTimeout:-1 tag:0];
}

#pragma mark - GCDAsyncSocket Delegate

- (void)socket:(GCDAsyncSocket *)sender didReadData:(NSData *)data withTag:(long)tag {
    if (data != nil) {
        [self.buffer appendData:data];
    }
    
//    char *mdata = (char*)data.bytes;
//    for(int i = 0; i < data.length; i++)
//    {
//        printf("%s%02x", i == 0 ? "data:\n":",",mdata[i]);
//    }
//    printf("\n");
    
    if (self.messgageBodySize == 0 && self.buffer.length >= 4) {
        UInt8 firstByte = 0;
        [self.buffer getBytes:&firstByte length:1];
        UInt8 type = (firstByte >> 4) & 0x0F;
        if (type == 2) {
            [self p_dealWithHeartBeat];
            if ([self.delegate respondsToSelector:@selector(streamdidAuthentication:)]) {
                [self.delegate streamdidAuthentication:self];
            }
        } else if (type == 3) {
            
        } else if (type == 5){
            NSLog(@"收到心跳回执");
        } else if (type == 6) {
            
        }
        [self.buffer getBytes:&_messgageBodySize range:NSMakeRange(2, 2)];
        self.messgageBodySize = CFSwapInt16HostToBig(self.messgageBodySize);
        [self.buffer replaceBytesInRange:NSMakeRange(0, 4) withBytes:NULL length:0];
    }
    
    NSUInteger bufferLength = self.buffer.length;
    
    if (self.messgageBodySize > 0 && bufferLength >= self.messgageBodySize) {
        NSData *messageBody = [self.buffer subdataWithRange:NSMakeRange(0, self.messgageBodySize)];
        NSString *messageStirng = [[NSString alloc] initWithData:messageBody encoding:NSUTF8StringEncoding];
        [self p_dealWithReceiveMessageWithJson:messageStirng];
        [self.buffer replaceBytesInRange:NSMakeRange(0, self.messgageBodySize) withBytes:NULL length:0]; // 消费掉buffer。
        self.messgageBodySize = 0;
        if (self.buffer.length > 0) { // NOTE: 如果还有就需要接着读取。
            [self socket:sender didReadData:nil withTag:tag];
        }
    }else
    {//不能解析的数据直接返回给receiveData
        if([self.delegate respondsToSelector:@selector(stream:didReceiveData:)])
        {
            [self.delegate stream:self didReceiveData:[data copy]];
        }
    }
    [sender readDataWithTimeout:-1 tag:0];
}

- (void)socket:(GCDAsyncSocket *)sender didConnectToHost:(NSString *)host port:(UInt16)port {
    _streamConnectionState = AW2StreamConnectionStateConnected;
    if (self.delegate && [self.delegate respondsToSelector:@selector(streamDidConnect:)])  {
        [self.delegate streamDidConnect:self];
    }
}

/**
 *  都在errno.h 文件中有描述
 
 *  Domain=NSPOSIXErrorDomain Code = 51 "Network is unreachable" 当没有网络 尝试连接被报这个错误
 *  Domain=NSPOSIXErrorDomain Code = 54 "Connection reset by peer" 连接复位 "在连接断开后的读和写操作引起的" 一般出现这个情况就只能重连服务器。
 *  Domain=NSPOSIXErrorDomain Code = 57 "Socket is not connected"  网络立即被关闭的。比如直接打开飞行模式，关闭WIFI等等。
 *  Domain=NSPOSIXErrorDomain Code = 60 "Operation timed out"
 *  Domain=NSPOSIXErrorDomain Code = 61 "Connection refused"
 *  Domain=NSPOSIXErrorDomain Code = 65 "No route to host"
 */
- (void)socketDidDisconnect:(GCDAsyncSocket *)sock withError:(NSError *)error {
    _streamConnectionState = AW2StreamConnectionStateDisconnected;
    
    if ([self p_isShouldNotNormalCancelledWithError:error]) {
        self.normalCancelled = NO;
    }
    if ([self p_isShouldNormalCancelledWithError:error]) {
        self.normalCancelled = YES;
    }
    if (self.delegate && [self.delegate respondsToSelector:@selector(stream:didDisconnectWithError:)]) {
        [self.delegate stream:self didDisconnectWithError:error];
    }
}

/**
 *  只要是YES 就重新连接IM。
 */
- (BOOL)p_isShouldNotNormalCancelledWithError:(NSError *)error {
    return [self p_isPOSIXErrorDomainWithError:error]
            && ([self p_isResetConnectionWithCode:error.code]
                || [self p_isTimeoutConnectionWithCode:error.code]
                || [self p_isRefusedConnectionWithCode:error.code]);
}

/**
 *  目前没有网络的情况下就不需要重连了。
 *  比如主动断开网络。
 */
- (BOOL)p_isShouldNormalCancelledWithError:(NSError *)error {
    return [self p_isPOSIXErrorDomainWithError:error]
            && [self p_isClosedConnectionWithCode:error.code];
}

- (BOOL)p_isPOSIXErrorDomainWithError:(NSError *)error {
    return [error.domain isEqualToString:NSPOSIXErrorDomain];
}

- (BOOL)p_isResetConnectionWithCode:(UInt64)code {
    return code == ECONNRESET;
}

- (BOOL)p_isTimeoutConnectionWithCode:(UInt64)code {
    return code == ETIMEDOUT;
}

- (BOOL)p_isRefusedConnectionWithCode:(UInt64)code {
    return code == ECONNREFUSED;
}

- (BOOL)p_isClosedConnectionWithCode:(UInt64)code {
    return code == ENOTCONN;
}

#pragma mark - Private Methods

- (void)p_dealWithHeartBeat {
    [self.heartBeatTimer invalidate];
    self.heartBeatTimer = [NSTimer timerWithTimeInterval:kHeartBeatTimerInterval target:self selector:@selector(p_sendHeartBeatMessge) userInfo:nil repeats:YES];
    [[NSRunLoop mainRunLoop] addTimer:self.heartBeatTimer forMode:NSDefaultRunLoopMode];
    [self.heartBeatTimer fire];
}

- (void)p_sendHeartBeatMessge {
    self.sendingPingTimestamp = [ATWUtil dateOfServerTimestamp];
    [self sendMessageData:[NSData data] withProtocolType:AWProtocolPingType];
}

- (void)p_removeHeartBeat {
    if (self.heartBeatTimer != nil && [self.heartBeatTimer isValid]) {
        [self.heartBeatTimer invalidate];
        self.heartBeatTimer = nil;
    }
}

- (void)p_dealWithReceiveMessageWithJson:(NSString *)jsonString {
    if ([self.delegate respondsToSelector:@selector(stream:didReceiveMessage:)]) {
        NSDictionary * message = [NSJSONSerialization JSONObjectWithData:[jsonString dataUsingEncoding:NSUTF8StringEncoding] options:NSJSONReadingMutableContainers error:nil];
        
        if (message[AWMessageKTimestamp] && message[AWMessageKTimezone]) { // NOTE: 处理消息时间差。
            
            UInt64 different = [message[AWMessageKTimestamp] longLongValue] - (UInt64)(([ATWUtil dateOfCurrentTimestamp] - self.sendingPingTimestamp) * .5) - self.sendingPingTimestamp;
            NSLog(@"时间差 = %lld",different);
            [ATWImApplication sharedInstance].messageTimeDifference = different;
            return;
        }
        
        AWMessage *resolverMessage = [AWMessageResolver resolveIfPossibleWithDictionary:message];
        
        if(resolverMessage)
        {//如果能够解析到信息
            [self.delegate stream:self didReceiveMessage:resolverMessage];
        }
        else
        {//否则直接返回数据
            if([self.delegate respondsToSelector:@selector(stream:didReceiveData:)])
            {
                [self.delegate stream:self didReceiveData:self.buffer.copy];
            }
        }
    }
}

- (void)normalCancelled {
    self.normalCancelled = YES;
}

- (void)abnormalCancelled {
    self.normalCancelled = NO;
}

/**
 *  修改Socket连接状态，并断开连接，移除心跳，目前AWProtocolDisConnectType 15 类型没有作用。
 */
- (void)disConnection {
    _streamConnectionState = AW2StreamConnectionStateDisconnected;
    [self normalCancelled];
    [self.theSocket disconnect];
    [self p_removeHeartBeat];
    [self sendMessageData:[NSData data] withProtocolType:AWProtocolDisConnectType];
}

- (void)connection {
    [self p_connectionStream];
}

#pragma mark - Getter Methods

- (NSMutableData *)buffer {
    if (_buffer == nil) {
        _buffer = [NSMutableData data];
    }
    return _buffer;
}

@end
